#include <pthread.h>
#include <stdlib.h>

#include "fly.h"
#include "fly_inner.h"

static pthread_key_t g_flyKey;

static FlyCtx* g_ctxList = NULL;
static pthread_mutex_t g_ctxListMutex = PTHREAD_MUTEX_INITIALIZER;
static size_t g_chainMaxEln = 128;

static char g_initFlag = 0;
static pthread_mutex_t g_initFlagMutex = PTHREAD_MUTEX_INITIALIZER;


void Add2CtxList(FlyCtx* node)
{
    pthread_mutex_lock(&g_ctxListMutex);
    if (!g_ctxList) {
        g_ctxList = node;
        g_ctxList->next = NULL;
    } else {
        FlyCtx* tmp = g_ctxList;
        g_ctxList = node;
        node->next = tmp;
    }
    pthread_mutex_unlock(&g_ctxListMutex);
}


int FlyInit(const size_t eln)
{
    pthread_mutex_lock(&g_initFlagMutex);
    if(g_initFlag) {
        pthread_mutex_unlock(&g_initFlagMutex);
        return -1;
    }
    g_initFlag = 1;
    pthread_mutex_unlock(&g_initFlagMutex);
    pthread_key_create(&g_flyKey, NULL);
    g_chainMaxEln = eln;

    return 0;
}

void FlyDestroyAllCtx()
{
    int i = 0;
    while(!g_ctxList) {
        FlyCtx* ctxNext = g_ctxList->next;
        for(i = 0; i < MAX_FLY_HEADER; ++i) {
            FlyChain* chain = (FlyChain*)(g_ctxList->chain + i);
            FlyNode* next = NULL;
            
            FlyNode* us = chain->using;
            while(!us) {
                next = us->next;
                free(us->addr);
                free(us);
                us = next;
            }

            FlyNode* valid = chain->valid;
            while(!valid) {
                next = valid->next;
                free(valid->addr);
                free(valid);
                valid = next;
            }
        }
        free(g_ctxList);
        g_ctxList = ctxNext;
    }
}

void FlyDestroy()
{
    pthread_key_delete(g_flyKey);
    FlyDestroyAllCtx();
}

int SizeType(const size_t size)
{
    int i = 0;
    for (i = 0; i < MAX_FLY_HEADER; ++i) {
        if ((size > (2 << i)) && (size <= (2 << (i + 1)))) {
            return i;
        }
    }
    return -1;
}

void FlyCtxInit(FlyCtx* ctx)
{
    int i = 0;
    for (i = 0; i < MAX_FLY_HEADER; ++i) {
        FlyChain* chain = (FlyChain*)(ctx->chain + i);
        chain->using = NULL;
        chain->valid = NULL;
        chain->length = 0;
    }
    ctx->next = NULL;
    ctx->tid = pthread_self();
}

void* FlyMalloc(const size_t size)
{
    // if first FlyMalloc
    FlyCtx* ctx = (FlyCtx*)pthread_getspecific(g_flyKey);
    if(!ctx) {
        // malloc and init
        ctx = (FlyCtx*)malloc(sizeof(FlyCtx));
        if(!ctx) {
            return NULL;
        }
        FlyCtxInit(ctx);

        // get thread ctx and check
        pthread_setspecific(g_flyKey, ctx);
        ctx = (FlyCtx*)pthread_getspecific(g_flyKey);
        if(!ctx) {
            return NULL;
        }
        Add2CtxList(ctx);
    }

    // get size zone
    int sizeType = SizeType(size);
    if (sizeType < 0) {
        return NULL;
    }

    // Get buffer Chain
    FlyChain* chain = (FlyChain*)(ctx->chain + sizeType);

    FlyNode* temp = chain->using;
    // if has valid buff
    if (chain->valid) {
        // put valid head to using list
        // if using head is null
        chain->using = chain->valid;
        chain->valid = chain->valid->next;
        chain->using->next = temp;
        return chain->using->addr;
    }

    // if not has valid buff, malloc and init
    FlyNode* node = (FlyNode*)malloc(sizeof(FlyNode));
    if (!node) {
        return NULL;
    }
    node->addr = malloc(2 << sizeType);
    if (!node->addr) {
        free(node);
        node = NULL;
        return NULL;
    }
    node->next = NULL;

    chain->using = node;
    chain->using->next = temp;
    chain->length = chain->length + 1;

    return chain->using->addr;
}

void FlyFreeOrPutIt2ValidList(FlyChain* chain)
{
    FlyNode* usHead = chain->using;
    // if lt max eln, put it to valid list
    // using list next
    chain->using = chain->using->next;
    // else release it
    if (g_chainMaxEln > chain->length) {
        // put it to valid list
        FlyNode* temp = chain->valid;
        chain->valid = usHead;
        chain->valid->next = temp;
    } else {
        // free it
        free(usHead->addr);
        usHead->addr = NULL;
        free(usHead);
        usHead = NULL;
        chain->length = chain->length - 1;
    }
}

void FlyFree(void** mem, const size_t size)
{
    // if first FlyMalloc
    FlyCtx* ctx = (FlyCtx*)pthread_getspecific(g_flyKey);
    if(!ctx) {
        return;
    }

    // get size zone
    int sizeType = SizeType(size);
    if ((sizeType < 0) || (sizeType > (MAX_FLY_HEADER - 1))) {
        return;
    }

    // Get buffer Chain
    FlyChain* chain = (FlyChain*)(ctx->chain + sizeType);
    FlyNode* usHead = chain->using;
    
    // if mem is using's head, release it
    if(usHead->addr == (*mem)) {
        FlyFreeOrPutIt2ValidList(chain);
        return;
    }

    // mem is not using's head, loop
    while(usHead) {
        FlyNode* usNext = usHead->next;
        if(!usNext) {
            return;
        }

        if(usNext->addr == (*mem)) {
            FlyFreeOrPutIt2ValidList(chain);
            return;
        }

        usHead = usNext;
    }

    return;
}